Learn in 10 minutes

Learn in 10 minutes

Go in 10 Minuten lernen

Go (auch bekannt als Golang) ist eine statisch typisierte, kompilierte Programmiersprache, die bei Google entwickelt wurde. Sie ist bekannt für ihre Einfachheit, Effizienz und hervorragende Unterstützung für Nebenläufigkeit. Dieses Tutorial hilft dir, schnell Go-Programmierung zu lernen.

1. Dein erstes Go-Programm schreiben

Beginnen wir mit einem einfachen Programm. Erstelle eine Datei namens hello.go und gib folgenden Code ein:

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Speichere die Datei und führe folgenden Befehl im Terminal aus:

go run hello.go

Die Ausgabe wird sein:

Hello, World!

Dieses einfache Programm demonstriert die grundlegende Struktur von Go:

  • package main deklariert den Paketnamen
  • import "fmt" importiert das Format-Paket für Ein-/Ausgabe-Operationen
  • func main() ist der Einstiegspunkt des Programms
  • fmt.Println() gibt Text auf der Konsole aus

2. Grundlegende Syntax

Go hat eine saubere und einfache Syntax. Im Gegensatz zu Python verwendet Go geschweifte Klammern {} zur Definition von Codeblöcken und erfordert Semikolons am Ende von Anweisungen (obwohl sie normalerweise automatisch eingefügt werden).

// Dies ist ein einzeiliger Kommentar
fmt.Println("Hello, World!")

/*
Dies ist ein mehrzeiliger Kommentar,
der sich über mehrere Zeilen erstreckt
*/

Grundlegende Syntax-Regeln in Go:

  • Codeblöcke: Werden durch geschweifte Klammern {} definiert
  • Kommentare: Einzeilige Kommentare beginnen mit //, mehrzeilige mit /* */
  • Anweisungen: Enden mit Semikolons (automatisch eingefügt)
  • Paket-Deklaration: Jede Datei beginnt mit einer Paket-Deklaration

3. Variablen und Datentypen

Go ist statisch typisiert, was bedeutet, dass du Variablentypen deklarieren musst. Allerdings unterstützt Go Typinferenz mit dem := Operator.

Methoden zur Variablendeklaration:

// Explizite Typdeklaration
var name string = "John"
var age int = 25

// Typinferenz
name := "John"
age := 25

// Mehrfache Variablendeklaration
var x, y int = 10, 20
x, y := 10, 20

Hauptgrunddatentypen von Go:

  • Ganzzahltypen: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr
  • Gleitkommatypen: float32, float64
  • String: string
  • Boolean: bool
  • Komplexe Typen: complex64, complex128

3.1 Numerische Typen

Go bietet verschiedene numerische Typen für verschiedene Anwendungsfälle:

// Ganzzahltypen
var age int = 25
var smallNumber int8 = 127
var largeNumber int64 = 9223372036854775807

// Gleitkommatypen
var temperature float32 = 36.5
var pi float64 = 3.14159265359

// Komplexe Zahlen
var complexNum complex64 = 3 + 4i

3.2 String-Typ

Strings in Go sind Byte-Sequenzen und sind unveränderlich:

// String-Deklaration
var greeting string = "Hello, Go!"
name := "Alice"

// String-Operationen
fmt.Println(len(greeting))        // String-Länge
fmt.Println(greeting[0])          // Auf erstes Zeichen zugreifen (Byte)
fmt.Println(greeting[0:5])        // String-Slicing
fmt.Println(strings.ToUpper(name)) // In Großbuchstaben umwandeln

3.3 Boolean-Typ

Der Boolean-Typ hat zwei Werte: true und false:

var isActive bool = true
var isComplete bool = false

// Boolean-Operationen
result1 := true && false  // false
result2 := true || false  // true
result3 := !true          // false

4. Konstanten

Konstanten werden mit dem const Schlüsselwort deklariert und können nicht geändert werden:

const Pi = 3.14159
const MaxUsers = 1000

// Mehrere Konstanten
const (
    StatusOK = 200
    StatusNotFound = 404
    StatusError = 500
)

// Typisierte Konstanten
const Version string = "1.0.0"

5. Datenstrukturen

Go bietet mehrere eingebaute Datenstrukturen zum Speichern und Manipulieren von Daten.

5.1 Arrays

Arrays sind feste Sequenzen von Elementen desselben Typs:

// Array-Deklaration
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
names := [3]string{"Alice", "Bob", "Charlie"}

// Auf Elemente zugreifen
fmt.Println(numbers[0])  // 1
numbers[0] = 10         // Element ändern

// Array-Länge
fmt.Println(len(numbers)) // 5

5.2 Slices

Slices sind dynamische Arrays, die wachsen und schrumpfen können:

// Slice-Deklaration
numbers := []int{1, 2, 3, 4, 5}
var emptySlice []int

// Slices aus Arrays erstellen
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4]  // [2, 3, 4]

// Slice-Operationen
numbers = append(numbers, 6)      // Element hinzufügen
numbers = append(numbers, 7, 8, 9) // Mehrere Elemente hinzufügen

// Slice-Kapazität und Länge
fmt.Println(len(numbers)) // Länge: 9
fmt.Println(cap(numbers)) // Kapazität: 10 (kann variieren)

5.3 Maps

Maps sind ungeordnete Sammlungen von Schlüssel-Wert-Paaren:

// Map-Deklaration
student := map[string]interface{}{
    "name": "John",
    "age":  20,
    "major": "Computer Science",
}

// Alternative Deklaration
var scores map[string]int = make(map[string]int)
scores["math"] = 95
scores["science"] = 88

// Zugreifen und Ändern
fmt.Println(student["name"])
student["age"] = 21
student["gpa"] = 3.8

// Sicherer Zugriff
if phone, exists := student["phone"]; exists {
    fmt.Println(phone)
} else {
    fmt.Println("Telefon nicht angegeben")
}

// Über Map iterieren
for key, value := range student {
    fmt.Printf("%s: %v\n", key, value)
}

5.4 Structs

Structs sind Sammlungen von Feldern, die verschiedene Typen haben können:

// Struct-Definition
type Person struct {
    Name string
    Age  int
    City string
}

// Struct-Instanzen erstellen
person1 := Person{"Alice", 25, "New York"}
person2 := Person{
    Name: "Bob",
    Age:  30,
    City: "London",
}

// Auf Felder zugreifen
fmt.Println(person1.Name)
person1.Age = 26

6. Operationen und Operatoren

Go bietet einen umfangreichen Satz von Operatoren für verschiedene Berechnungen und Vergleiche.

  • Arithmetische Operatoren: +, -, *, /, % (Modulo)
  • Vergleichsoperatoren: ==, !=, >, <, >=, <=
  • Logische Operatoren: &&, ||, !
  • Bitweise Operatoren: &, |, ^, <<, >>
  • Zuweisungsoperatoren: =, +=, -=, *=, /=

6.1 Arithmetische Operatoren

a, b := 10, 3

fmt.Printf("Addition: %d\n", a + b)      // 13
fmt.Printf("Subtraktion: %d\n", a - b)   // 7
fmt.Printf("Multiplikation: %d\n", a * b)  // 30
fmt.Printf("Division: %d\n", a / b)      // 3
fmt.Printf("Modulo: %d\n", a % b)      // 1

6.2 Vergleichsoperatoren

x, y := 5, 10

fmt.Printf("Gleich: %t\n", x == y)     // false
fmt.Printf("Ungleich: %t\n", x != y) // true
fmt.Printf("Größer als: %t\n", x > y)  // false
fmt.Printf("Kleiner als: %t\n", x < y)  // true

6.3 Logische Operatoren

a, b := true, false

fmt.Printf("UND-Operation: %t\n", a && b)  // false
fmt.Printf("ODER-Operation: %t\n", a || b)    // true
fmt.Printf("NICHT-Operation: %t\n", !a)    // false

7. Kontrollfluss

Go bietet mehrere Kontrollfluss-Anweisungen zur Steuerung der Programmausführung.

7.1 if-Anweisungen

age := 20
if age >= 18 {
    fmt.Println("Erwachsener")
} else if age >= 13 {
    fmt.Println("Jugendlicher")
} else {
    fmt.Println("Kind")
}

// if mit kurzer Anweisung
if score := 85; score >= 90 {
    fmt.Println("Note: A")
} else if score >= 80 {
    fmt.Println("Note: B")
} else {
    fmt.Println("Note: C")
}

7.2 for-Schleifen

Go hat nur eine Schleifenkonstruktion: for

// Grundlegende for-Schleife
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// While-artige Schleife
count := 0
for count < 5 {
    fmt.Println(count)
    count++
}

// Endlosschleife
for {
    fmt.Println("Dies läuft für immer")
    break // Verwende break zum Verlassen
}

// Range-Schleife (für Slices, Arrays, Maps)
fruits := []string{"Apfel", "Banane", "Kirsche"}
for index, fruit := range fruits {
    fmt.Printf("%d: %s\n", index, fruit)
}

7.3 switch-Anweisungen

day := "Monday"
switch day {
case "Monday":
    fmt.Println("Wochenanfang")
case "Friday":
    fmt.Println("Wochenende naht")
case "Saturday", "Sunday":
    fmt.Println("Wochenende!")
default:
    fmt.Println("Normaler Tag")
}

// Switch ohne Ausdruck
score := 85
switch {
case score >= 90:
    fmt.Println("Note: A")
case score >= 80:
    fmt.Println("Note: B")
case score >= 70:
    fmt.Println("Note: C")
default:
    fmt.Println("Note: F")
}

8. Funktionen

Funktionen in Go sind First-Class-Citizens und unterstützen mehrere Rückgabewerte.

8.1 Grundlegende Funktionen

func greet(name string) string {
    return "Hello, " + name + "!"
}

// Funktion aufrufen
message := greet("John")
fmt.Println(message)

8.2 Mehrere Rückgabewerte

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("Division durch Null nicht möglich")
    }
    return a / b, nil
}

// Mehrere Rückgabewerte verwenden
result, err := divide(10, 2)
if err != nil {
    fmt.Println("Fehler:", err)
} else {
    fmt.Println("Ergebnis:", result)
}

8.3 Benannte Rückgabewerte

func calculateRectangle(width, height float64) (area float64, perimeter float64) {
    area = width * height
    perimeter = 2 * (width + height)
    return // "naked return"
}

area, perimeter := calculateRectangle(5, 3)
fmt.Printf("Fläche: %.2f, Umfang: %.2f\n", area, perimeter)

8.4 Variadische Funktionen

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

fmt.Println(sum(1, 2, 3, 4))  // 10
fmt.Println(sum(5, 10, 15))   // 30

9. Zeiger

Go hat Zeiger, aber mit einfacherer Syntax als C/C++:

func modifyValue(x *int) {
    *x = *x * 2
}

func main() {
    value := 10
    fmt.Println("Vorher:", value) // 10

    modifyValue(&value)
    fmt.Println("Nachher:", value)  // 20
}

10. Methoden

Methoden sind Funktionen mit einem Empfänger-Argument:

type Rectangle struct {
    Width  float64
    Height float64
}

// Methode mit Wertempfänger
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Methode mit Zeigerempfänger
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

rect := Rectangle{Width: 5, Height: 3}
fmt.Println("Fläche:", rect.Area()) // 15

rect.Scale(2)
fmt.Println("Skalierte Fläche:", rect.Area()) // 60

11. Interfaces

Interfaces definieren Methodensignaturen, die Typen implementieren können:

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

func printShapeInfo(s Shape) {
    fmt.Printf("Fläche: %.2f, Umfang: %.2f\n", s.Area(), s.Perimeter())
}

circle := Circle{Radius: 5}
printShapeInfo(circle)

12. Fehlerbehandlung

Go verwendet explizite Fehlerbehandlung statt Ausnahmen:

func readFile(filename string) (string, error) {
    data, err := os.ReadFile(filename)
    if err != nil {
        return "", fmt.Errorf("Datei %s konnte nicht gelesen werden: %w", filename, err)
    }
    return string(data), nil
}

content, err := readFile("example.txt")
if err != nil {
    fmt.Println("Fehler:", err)
    return
}
fmt.Println("Inhalt:", content)

13. Nebenläufigkeit mit Goroutines

Goroutines sind leichtgewichtige Threads, die von der Go-Laufzeitumgebung verwaltet werden:

func worker(id int) {
    for i := 0; i < 3; i++ {
        fmt.Printf("Worker %d: %d\n", id, i)
        time.Sleep(time.Millisecond * 100)
    }
}

func main() {
    // Mehrere Goroutines starten
    for i := 1; i <= 3; i++ {
        go worker(i)
    }

    // Auf Abschluss der Goroutines warten
    time.Sleep(time.Second)
    fmt.Println("Alle Worker abgeschlossen")
}

14. Kanäle

Kanäle werden für die Kommunikation zwischen Goroutines verwendet:

func producer(ch chan<- int) {
    for i := 0; i < 5; i++ {
        ch <- i // Wert an Kanal senden
        time.Sleep(time.Millisecond * 100)
    }
    close(ch) // Kanal schließen, wenn fertig
}

func consumer(ch <-chan int) {
    for value := range ch {
        fmt.Println("Empfangen:", value)
    }
}

func main() {
    ch := make(chan int, 3) // Gepufferter Kanal

    go producer(ch)
    consumer(ch)

    fmt.Println("Kanal-Kommunikation abgeschlossen")
}

15. Dateioperationen

Go bietet einfache Methoden zum Lesen und Schreiben von Dateien:

// Dateien lesen
data, err := os.ReadFile("example.txt")
if err != nil {
    fmt.Println("Fehler beim Lesen der Datei:", err)
    return
}
fmt.Println("Dateiinhalt:", string(data))

// Dateien schreiben
content := "Hello, Go!\n"
err = os.WriteFile("output.txt", []byte(content), 0644)
if err != nil {
    fmt.Println("Fehler beim Schreiben der Datei:", err)
    return
}
fmt.Println("Datei erfolgreich geschrieben")

16. Pakete und Module

Go-Module verwalten Abhängigkeiten und Paketversionen:

// Standardbibliothekspakete importieren
import (
    "fmt"
    "math"
    "strings"
)

func main() {
    fmt.Println(math.Sqrt(16))        // 4
    fmt.Println(strings.ToUpper("go")) // GO
}

Um dein eigenes Paket zu erstellen, erstelle ein Verzeichnis mit deinem Paketnamen und exportiere Funktionen, indem du ihre Namen groß schreibst.

17. Testen

Go hat eingebaute Testunterstützung:

// In Datei math_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(2, 3)
    expected := 5
    if result != expected {
        t.Errorf("add(2, 3) = %d; erwartet %d", result, expected)
    }
}

func add(a, b int) int {
    return a + b
}

Führe Tests aus mit: go test

18. Best Practices

  • Verwende gofmt zum Formatieren deines Codes
  • Folge den Go-Namenskonventionen (camelCase für Variablen, PascalCase für Exporte)
  • Behandle Fehler explizit
  • Verwende Interfaces für Abstraktion
  • Bevorzuge Komposition gegenüber Vererbung
  • Schreibe umfassende Tests
  • Verwende die Standardbibliothek wann immer möglich

Dieses Tutorial deckt die wesentlichen Funktionen der Go-Programmierung ab. Mit Übung wirst du in der Lage sein, effiziente und nebenläufige Anwendungen mit den leistungsstarken Funktionen von Go zu erstellen.